package com.boot2.core.utils;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.boot2.core.SysProperties;
import com.boot2.core.constant.CoreConstant;
import com.boot2.core.constant.CoreUrlConstant;
import com.boot2.core.constant.HttpConstant;
import com.boot2.core.feign.FeignBasicAuthRequestInterceptor;
import com.boot2.core.model.dto.ClientDeviceInfo;
import com.boot2.core.web.RequestContext;
import com.google.common.collect.ImmutableMap;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

/**
 * 大量基于servlet和request，以及请求路径的封装，
 *
 * @author zhangweilin
 * @version V1.0
 * @date 2016年12月15日 下午4:44:38
 * @description
 */
@CommonsLog
@Component
public class ServletUtil {
    public static final String urlKey = "url";
    public static final String postMapKey = "postMap";
    /**
     * 是否是ajax请求,用来人为标记,
     * 如果不是ajax请求，但是要按ajax请求的方式处理，如从node转发的
     */
    public static final String isAjaxKey = "isAjax";

    private static SysProperties sysProperties;

    @Autowired
    public void setSysProperties(SysProperties sysProperties) {
        ServletUtil.sysProperties = sysProperties;
    }

    /**
     * 获取上一次的登陆 路径
     *
     * @param request
     * @return
     */
    public static String getLastPath(HttpServletRequest request) {
        String lastPath = request.getHeader("Referer");
        request.setAttribute("lastPath", lastPath);
        return lastPath;
    }

    public static Map<?, ?> getParameters(HttpServletRequest request) {
        return request.getParameterMap();
    }

    public static String getParameter(String key) {
        HttpServletRequest request = RequestContext.getRequest();
        return request.getParameter(key);
    }

    /**
     * 系统默认的获取参数map,value是个数组，实际大多数却不是，所以人工重组一下
     *
     * @return
     */
    public static Map<?, ?> getParameters0() {
        return getParameters0(RequestContext.getRequest());
    }

    public static Map<?, ?> getParameters0(HttpServletRequest request) {
        Enumeration<String> parameterNames = request.getParameterNames();
        Map<String, Object> params = new HashMap<String, Object>();
        while (parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            String value = request.getParameter(name);
            params.put(name, value);
        }
        return params;
    }

    public static String getParameterString(HttpServletRequest request) {
        Map<?, ?> paramMap = getParameters(request);
        return getParameterString(paramMap);
    }

    /**
     * 拼装成查询字符串
     *
     * @param ps
     * @param excludedKey 要排除的特殊key
     * @return
     */
    public static String getParameterString(Map<?, ?> ps, String... excludedKey) {
        if (CheckUtil.isEmpty(ps)) {
            return "";
        }
        return UrlEncryptUtil.getParameterString(ps, excludedKey);
    }

    /**
     * 替换参数
     *
     * @param url
     * @param paramMap
     * @return
     */
    public static String replaceParamter(String url, Map<String, String> paramMap) {
        if (CollectionUtils.isEmpty(paramMap)) {
            return url;
        }

        Set<Entry<String, String>> entrySet = paramMap.entrySet();
        for (Entry<String, String> entry : entrySet) {
            url = replaceParamterReg(url, entry.getKey(), entry.getValue());
        }
        return url;
    }

    /**
     * 用遍历替换参数值
     *
     * @param url
     * @param name
     * @param value
     * @return
     */
    public static String replaceParamter(String url, String name, String value) {
        if (StringUtils.isNotBlank(url) && StringUtils.isNotBlank(value)) {
            int index = url.indexOf(name + "=");
            if (index != -1) {
                StringBuilder sb = new StringBuilder();
                sb.append(url.substring(0, index)).append(name + "=").append(value);
                int idx = url.indexOf("&", index);
                if (idx != -1) {
                    sb.append(url.substring(idx));
                }
                url = sb.toString();
            }
        }
        return url;
    }

    /**
     * 用正则替换参数值
     *
     * @param url
     * @param name
     * @param value
     * @return
     */
    public static String replaceParamterReg(String url, String name, String value) {
        if (StringUtils.isNotBlank(url) && StringUtils.isNotBlank(value)) {
            url = url.replaceAll(name + "=[^&]*", name + "=" + value);
        }
        return url;
    }

    /**
     * 用正则移除某个参数
     *
     * @param url
     * @param name
     * @param value
     * @return
     */
    public static String removeParamterReg(String url, String name) {
        if (StringUtils.isNotBlank(url)) {
            url = url.replaceAll("(" + name + "=[^&]*)", "").replace("&&", "&");
            if (url.endsWith("&")) {
                url = url.substring(0, url.length() - 1);
            }
            if (url.endsWith("?")) {
                url = url.substring(0, url.length() - 1);
            }
        }
        return url;
    }

    /**
     * 追加或替换参数， 比如用在追加替换token
     *
     * @param url
     * @param name
     * @param value
     * @return
     */
    public static String addOrReplaceParamter(String url, String name, String value) {
        if (url.matches(".*" + name + "=[^&].*")) {
            url = ServletUtil.replaceParamterReg(url, name, value);
        } else {
            Map<String, Object> paramMap = new HashMap<>(1);
            paramMap.put(name, value);
            url = addParamter(url, paramMap);
        }
        return url;
    }

    /**
     * url追加参数，纯追加
     *
     * @param url
     * @param param
     * @return
     */
    public static String addParamter(String url, Map<String, Object> paramMap) {
        if (StringUtils.isEmpty(url)) {
            return url;
        }

        if (CollectionUtils.isEmpty(paramMap)) {
            return url;
        }

        // 要追加的参数串(只适合get的url部分)
        StringBuffer sb2 = new StringBuffer("");
        StringBuffer sb = new StringBuffer(url);
        Set<Entry<String, Object>> entrySet = paramMap.entrySet();
        for (Entry<String, Object> entry : entrySet) {
            sb2.append("&").append(entry.getKey()).append("=").append(entry.getValue());
        }
        String param = sb2.toString();
        // 如果有参数，直接追加即可
        if (url.contains("?")) {
            // 先去掉尾部 & case: xxx?&,xxx?a=1&b=2&
            if (url.endsWith("&")) {
                url = url.substring(0, url.length() - 1);
            }
            // 如果移除最后一个后，还有，且还是以问号结束， 则剩下还有参数，直接追加即可

            // 如果仅仅只有问号却没有参数,则参数部分不能以&开头,否则直接追加即可
            if (url.endsWith("?")) {
                param = param.substring(1, param.length());
            }
        } else {
            param = param.replaceFirst("&", "?");
        }
        sb.append(param);
        url = sb.toString();
        return url;
    }

    /**
     * 获取请求方法
     *
     * @return
     */
    public static String getMethod() {
        return RequestContext.getRequest().getMethod();
    }

    /**
     * 获取当前请求的全部url和参数信息，最原始还原请求结构，包括url中的参数以post中的参数，以map方式返回，key为url的对应的值，
     * 是地址栏中的url,key为postMap对应的值，是post过来的值,以数组方式组成
     *
     * @param request
     * @return
     */
    public static Map<String, Object> getPostFullUrlAndParam() {
        return getPostFullUrlAndParam(RequestContext.getRequest());
    }

    /**
     * 获取当前请求的全部url和参数信息，最原始还原请求结构，包括url中的参数以post中的参数，以map方式返回，key为url的对应的值，
     * 是地址栏中的url,key为postMap对应的值，是post过来的值,以数组方式组成
     *
     * @param request
     * @return
     */
    @SuppressWarnings("deprecation")
    public static Map<String, Object> getPostFullUrlAndParam(HttpServletRequest request) {
        // 获取到get部分的参数
        // String uri = request.getRequestURI();
        // queryString里提取出的参数map
        Map<String, String[]> paramMap0 = new HashMap<>();
        // 提取出的post过来的参数
        Map<String, String[]> paramMap1 = new HashMap<>();
        String queryString = request.getQueryString();
        queryString = queryString == null ? "" : queryString;
        // System.out.println("uri: " + uri);
        String[] nameValuePairArr = queryString.split("&");
        for (String str : nameValuePairArr) {
            String[] nameValuePair = str.split("=");
            if (null == nameValuePair || nameValuePair.length < 2) {
                continue;
            }

            String name = nameValuePair[0];
            String value = nameValuePair[1];

            String[] valueArr = paramMap0.get(name);
            if (null == valueArr) {
                paramMap0.put(name, new String[]{value});
            } else {
                paramMap0.put(name, ArrayUtils.add(valueArr, value));
            }
        }

        Set<Entry<String, String[]>> entrySet0 = paramMap0.entrySet();
        for (Entry<String, String[]> entry : entrySet0) {
            String key = entry.getKey();
            String[] value = entry.getValue();
        }

        Map<String, String[]> paramMap = (Map<String, String[]>) getParameters(request);
        Set<Entry<String, String[]>> entrySet = paramMap.entrySet();
        for (Entry<String, String[]> entry : entrySet) {
            // 从paramMap里提取出来的全部参数，包括get的和post的
            String key = entry.getKey();
            String[] value = entry.getValue();

            // 从queryString里提取出来的参数值，只有get的
            String[] valueArr2 = paramMap0.get(key);
            // 如果从queryString里提取出来后，不存在某个参数，说明此参数为post过来的
            if (null == valueArr2 || valueArr2.length == 0) {
                paramMap1.put(key, value);
            } else {
                // 如果存在，但是里面的数组值少于全部，则少掉的这一部分，为post过来的
                // List<String> value1List = Arrays.asList(value);此list不能做修改
                // 只有两个数组不相等，才需要遍历提取，如果相等，直接没有post数据
                if (!ArrayUtils.isEquals(value, valueArr2)) {

                    List<String> value1List = new ArrayList<>();
                    List<String> value2List = new ArrayList<>();
                    org.apache.commons.collections4.CollectionUtils.addAll(value1List, value);
                    org.apache.commons.collections4.CollectionUtils.addAll(value2List, valueArr2);

                    Iterator<String> iterator = value1List.iterator();
                    Iterator<String> iterator2 = value2List.iterator();
                    for (; iterator.hasNext(); ) {
                        String str = iterator.next();
                        for (; iterator2.hasNext(); ) {
                            String str2 = iterator2.next();
                            // System.out.println("str: " + str + " str2: " +
                            // str2);
                            if (str.equals(str2)) {
                                try {
                                    iterator.remove();
                                } catch (IllegalStateException e) {
                                    // e.printStackTrace();
                                    System.err.println("外层list移除失败， 参数中有相同值的数组，已忽略.." + str2);
                                }
                                iterator2.remove();
                                break;
                            }
                        }
                        // 迭代器，迭代后如果想再迭代，只能再赋值
                        iterator2 = value2List.iterator();
                    }
                    String[] value1 = value1List.toArray(new String[]{});
                    if (value1 != null && value1.length != 0) {
                        paramMap1.put(key, value1);
                    }
                }
            }
        }

        // ==post数据===
        Set<Entry<String, String[]>> entrySet1 = paramMap1.entrySet();
        for (Entry<String, String[]> entry : entrySet1) {
            String key = entry.getKey();
            String[] value = entry.getValue();
        }

        // ===原始的地址栏里地址==
        String url = getFullUrl();

        Map<String, Object> urlMap = new HashMap<>();
        urlMap.put(urlKey, url);
        if (!CollectionUtils.isEmpty(paramMap1)) {
            urlMap.put(postMapKey, paramMap1);
        }
        return urlMap;
    }

    /**
     * 获取当前请求的全路径(注意域名问题，有可能需要tomcat配置域名或nginx配置来配合)
     *
     * @param request
     * @return
     */
    public static String getFullUrl(HttpServletRequest request) {
        if (null == request) {
            return "";
        }
        String url = request.getHeader("fullurl");
        if (StringUtils.isEmpty(url)) {
            String actualFullUrl = getFullUrl(request, false);
//			log.warn("fullurl为空，请在nginx里配置如下: proxy_set_header fullUrl $scheme://$host$request_uri;uri: " + uri);
//			log.warn("现返回后端实际全地址: "+actualFullUrl);
            return actualFullUrl;
//			throw new IllegalArgumentException(
//					"fullurl为空，请在nginx里配置如下: proxy_set_header fullUrl $scheme://$host$request_uri;uri: " + uri);
        }
        // 处理被转义情况
        url = url.replace("amp;", "");
        log.debug("get fullurl: " + url);
        return url;
    }

    /**
     * 相对原生request中增强的uri,即把工程名contextPath去掉了
     *
     * @param request
     * @return
     */
    public static String getUri2(HttpServletRequest request) {
        String uri = request.getRequestURI();
        uri = uri.replaceFirst(request.getContextPath(), "");
        uri = uri.replaceFirst("/+", "/"); // remove muti "/"
        return uri;
    }

    /**
     * 获取绝对的uri,此基于浏览器里的绝对地址，基于nginx的自定义配置fullUrl
     *
     * @return
     */
    public static String getAbsoluteUri() {
        String fullUrl = getFullUrl();
        String uri = fullUrl.replace("http://", "").replace("https://", "").replace(getServerName(), "");
        return uri;
    }

    /**
     * 获取绝对的uri,此基于浏览器里的绝对地址
     *
     * @param fullUrl 全路径
     * @return
     */
    public static String getAbsoluteUri(String fullUrl) {
        String uri = fullUrl.replace("http://", "").replace("https://", "").replace(getServerName(), "");
        return uri;
    }

    /**
     * 获取当前请求的全路径(注意域名问题，有可能需要tomcat配置域名或nginx配置来配合)
     *
     * @param request
     * @param proxyServer 如果没有通过反向代理服务器做请求，那么就不能去除contextPath;
     * @return
     */
    public static String getFullUrl(HttpServletRequest request, boolean proxyServer) {
        String queryString = ServletUtil.getParameterString(request);
        String url = request.getRequestURL().toString();
        if (StringUtils.isNotEmpty(queryString)) {
            url = url + "?" + queryString;
        }

        // 如果没有通过反向代理服务器做请求，那么就不能去除contextPath;
        if (!proxyServer) {
            url = url.replaceFirst(request.getContextPath(), "");
        }

        if (url.endsWith("&")) {
            url = url.substring(0, url.length() - 1);
        }
        return url;
    }

    /**
     * 获取单个ip
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if ((ip == null) || (ip.length() == 0) || ("unknown".equalsIgnoreCase(ip))) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if ((ip == null) || (ip.length() == 0) || ("unknown".equalsIgnoreCase(ip))) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if ((ip == null) || (ip.length() == 0) || ("unknown".equalsIgnoreCase(ip))) {
            ip = request.getRemoteAddr();
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        return ip;
    }

    /**
     * x-forwarded-for是ip累加，即如果一次调用，经过多次nginx，会导致每次的ip累加成一个数组字符串
     * @param request
     * @return
     */
    public static String getIps(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        return ip;
    }

    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        // ipAddress = request.getRemoteAddr();

        if (null != request) {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    ipAddress = getIntranetIP();
                }

            }
        } else {
            ipAddress = getIntranetIP();
        }

        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }

    private static String getIntranetIP() {
        String ipAddress;// 根据网卡取本机配置的IP
        InetAddress inet = null;
        try {
            inet = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        ipAddress = inet.getHostAddress();
        return ipAddress;
    }

    public static int getIpAddrInt(String ip) {
        String[] ipArr = ip.split("\\.");
        return (Integer.parseInt(ipArr[0]) * 256 * 256 * 256 + Integer.parseInt(ipArr[1]) * 256 * 256
                + Integer.parseInt(ipArr[2]) * 256 + Integer.parseInt(ipArr[3]));
    }

    public static String getBasePath(HttpServletRequest request) {
        String portPath = "";
        if (request.getServerPort() != 80) {
            portPath = ":" + request.getServerPort();
        }
        String path = request.getScheme() + "://" + request.getServerName() + portPath + request.getContextPath();
        return path;
    }

    public static String getServerName(HttpServletRequest request) {
        return request.getServerName();
    }

    public static String getContextPath(HttpServletRequest request) {
        return request.getContextPath();
    }

    public static String get2DoMainName(HttpServletRequest request) {
        String path = getServerName(request);
        String uesrName = "";
        if (path.contains(".")) {
            uesrName = path.substring(0, path.indexOf("."));
        }

        String active = EnvironmentUtil.get("spring.profiles.active");
        //非prod才需要替换
        if (!"prod".equals(active)) {
            //有时可能刚好二级域名是dev-或test开头-，所以prod-开头的，为保留域名，不能对外做业务二级域名
            if (uesrName.startsWith(active+"-")) {
                uesrName = uesrName.substring(active.length() + 1);
            }
        }
        return uesrName;
    }

    public static void deleteCookie(String key, HttpServletResponse response) {
        Cookie cookie = new Cookie(key, null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
    }

    public static void addCookie(String key, String value, int maxAge, HttpServletResponse response) {
        Cookie cookie = new Cookie(key, value);
        cookie.setMaxAge(maxAge);
        response.addCookie(cookie);
    }

    public static void addCookieByPath(String key, String value, String path, int maxAge) {
        addCookieByPath(key, value, path, maxAge, RequestContext.getResponse());
    }

    public static void addOrUpdateCookieByPath(String key, String value, String path, int maxAge) {
        Cookie cookie = getCookie(key);
        if (cookie == null) {
            addCookieByPath(key, value, path, maxAge);
        }else{
            cookie.setValue(value);
            cookie.setPath(path);
            cookie.setMaxAge(maxAge);
            RequestContext.getResponse().addCookie(cookie);
        }
    }

    public static void addCookieByPath(String key, String value, String path, int maxAge, HttpServletResponse response) {
        Cookie cookie = new Cookie(key, value);
        cookie.setMaxAge(maxAge);
        cookie.setPath(path);
        response.addCookie(cookie);
    }

    public static void addCookieByDomain(String key, String value, String domain, int maxAge) {
        Cookie cookie = new Cookie(key, value);
        cookie.setMaxAge(maxAge);
        cookie.setDomain(domain);
        RequestContext.getResponse().addCookie(cookie);
    }


    public static void addOrUpdateCookieByDomain(String key, String value, String domain, int maxAge) {
        Cookie cookie = getCookie(key);
        if (null == cookie) {
            addCookieByDomain(key, value, domain, maxAge);
        } else {
            cookie.setDomain(domain);
            cookie.setMaxAge(maxAge);
            RequestContext.getResponse().addCookie(cookie);
        }
    }

    public static void addCookie(String key, String value, String path, String domain, int maxAge,
                                 HttpServletResponse response) {
        Cookie cookie = new Cookie(key, value);
        cookie.setMaxAge(maxAge);
        cookie.setPath(path);
        cookie.setDomain(domain);
        response.addCookie(cookie);
    }

    public static String readCookie(String key, HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return null;
        }
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(key)) {
                return cookie.getValue();
            }
        }
        return null;
    }

    public static void printHeaders(HttpServletRequest request) {
        Enumeration<String> headNames = request.getHeaderNames();
        while (headNames.hasMoreElements()) {
            String headName = headNames.nextElement();
            System.out.println("headName: " + headName + " , value: " + request.getHeader(headName));
        }
    }

    /**
     * @param request
     * @return
     * @throws ServletException
     * @throws IOException
     */
    public static Map<String, String> getClientInfo(HttpServletRequest request) {
        Map<String, String> headInfoMap = getHeaderInfo(request);
        Map<String, String> cookieInfoMap = getCookieInfo(request);
        Map<String, String> infoMap = getClientRequestInfo(request);

        infoMap.putAll(headInfoMap);
        infoMap.putAll(cookieInfoMap);

        return infoMap;
    }

    public static Map<String, String>[] getClientInfoStat() {
        return getClientInfoStat(RequestContext.getRequest());
    }

    public static String getHeader(String headerKey) {
        return RequestContext.getRequest().getHeader(headerKey);
    }

    /**
     * @param request
     * @return
     * @throws ServletException
     * @throws IOException
     */
    public static Map<String, String>[] getClientInfoStat(HttpServletRequest request) {
        Map<String, String>[] infoMapArr = new Map[4];
        Map<String, String> headInfoMap = getHeaderInfo(request);

        // Cookie
        Map<String, String> cookieInfoMap = getCookieInfo(request);

        // base info
        Map<String, String> infoMap = getClientRequestInfo(request);

        infoMapArr[0] = headInfoMap;
        infoMapArr[1] = infoMap;
        infoMapArr[2] = cookieInfoMap;
        Object o = request.getAttribute(CoreConstant.loginUserInfo);
        if (o!=null) {
            infoMapArr[3] = ImmutableMap.of(CoreConstant.loginUserInfo,o.toString());
        }

        return infoMapArr;
    }

    private static Map<String, String> getClientRequestInfo(HttpServletRequest request) {
        Map<String, String> infoMap = new TreeMap<>();
        // info
        String getMethod = request.getMethod();// 获得客户端向服务器端传送数据的方法有GET、POST、PUT等类型
        infoMap.put("getMethod", getMethod);

        String getRequestURI = request.getRequestURI();// 获得发出请求字符串的客户端地址
        infoMap.put("getRequestURI", getRequestURI);

        String getServletPath = request.getServletPath();// 获得客户端所请求的脚本文件的文件路径
        infoMap.put("getServletPath", getServletPath);

        String getServerName = request.getServerName();// 获得服务器的名字
        infoMap.put("getServerName", getServerName);

        int getServerPort = request.getServerPort();// 获得服务器的端口号
        infoMap.put("getServerPort", getServerPort + "");

        String getRemoteAddr = request.getRemoteAddr();// 获得客户端的IP地址
        infoMap.put("getRemoteAddr", getRemoteAddr);

        String getRemoteHost = request.getRemoteHost();// 获得客户端电脑的名字，若失败，则返回客户端电脑的IP地址
        infoMap.put("getRemoteHost", getRemoteHost);

        String getProtocol = request.getProtocol();
        infoMap.put("getProtocol", getProtocol);

        String getCharacterEncoding = request.getCharacterEncoding();
        infoMap.put("getCharacterEncoding", getCharacterEncoding);

        int getContentLength = request.getContentLength();
        infoMap.put("getContentLength", getContentLength + "");

        String getContentType = request.getContentType();
        infoMap.put("getContentType", getContentType);

        String getContextPath = request.getContextPath();
        infoMap.put("getContextPath", getContextPath);

        String getLocalAddr = request.getLocalAddr();
        infoMap.put("getLocalAddr", getLocalAddr);

        String getLocalName = request.getLocalName();
        infoMap.put("getLocalName", getLocalName);

        int getLocalPort = request.getLocalPort();
        infoMap.put("getLocalPort", getLocalPort + "");

        String getPathInfo = request.getPathInfo();
        infoMap.put("getPathInfo", getPathInfo);

        String getPathTranslated = request.getPathTranslated();
        infoMap.put("getPathTranslated", getPathTranslated);

        String getQueryString = request.getQueryString();
        infoMap.put("getQueryString", getQueryString);

        // String getRealPath = request.getRealPath("");
        // infoMap.put("getRealPath", getRealPath);

        int getRemotePort = request.getRemotePort();
        infoMap.put("getRemotePort", getRemotePort + "");

        String getRemoteUser = request.getRemoteUser();
        infoMap.put("getRemoteUser", getRemoteUser);

        String getRequestedSessionId = request.getRequestedSessionId();
        infoMap.put("getRequestedSessionId", getRequestedSessionId);

        StringBuffer getRequestURL = request.getRequestURL();
        infoMap.put("getRequestURL", getRequestURL.toString());

        String getScheme = request.getScheme();
        infoMap.put("getScheme", getScheme);

        ServletContext getServletContext = request.getServletContext();
        infoMap.put("getServletContext.getContextPath", getServletContext.getContextPath());

        HttpSession getSession = request.getSession();
        infoMap.put("getSession.getId", getSession.getId());

        Principal getUserPrincipal = request.getUserPrincipal();
        if (null != getUserPrincipal) {
            infoMap.put("getUserPrincipal", getUserPrincipal.getName());
        }

        infoMap.put("getLastPath", ServletUtil.getLastPath(request));
        infoMap.put("getFullUrl", ServletUtil.getFullUrl(request));
        infoMap.put("getIp", ServletUtil.getIp(request));

        return infoMap;
    }

    public static Map<String, String> getCookieInfo() {
        return getCookieInfo(RequestContext.getRequest());
    }

    private static Map<String, String> getCookieInfo(HttpServletRequest request) {
        Map<String, String> cookieInfoMap = new TreeMap<>();
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                cookieInfoMap.put("cookie:[" + cookie.getName() + "]", cookie.getValue());
            }
        }
        return cookieInfoMap;
    }


    private static Map<String, String> getHeaderInfo(HttpServletRequest request) {
        Map<String, String> headInfoMap = new TreeMap<>();
        Enumeration<String> names = request.getHeaderNames();
        while (names != null & names.hasMoreElements()) {
            String name = names.nextElement();
            // System.out.println(name + ": " + request.getHeader(name));
            headInfoMap.put("head:[" + name + "]", request.getHeader(name));
        }
        return headInfoMap;
    }

    /**
     * 获取上一次的登陆 路径
     *
     * @param request
     * @return
     */
    public static String getLastPath() {
        return getLastPath(RequestContext.getRequest());
    }

    public static Map<?, ?> getParameters() {
        return getParameters(RequestContext.getRequest());
    }

    public static String getParameterString() {
        return getParameterString(RequestContext.getRequest());
    }

    /**
     * 获取当前请求的全路径(注意域名问题，有可能需要tomcat配置域名或nginx配置来配合)
     *
     * @param request
     * @return
     */
    public static String getFullUrl() {
        return getFullUrl(RequestContext.getRequest());
    }

    /**
     * 获取当前请求的全路径(注意域名问题，有可能需要tomcat配置域名或nginx配置来配合)
     *
     * @param request
     * @param proxyServer 如果没有通过反向代理服务器做请求，那么就不能去除contextPath;
     * @return
     */
    public static String getFullUrl(boolean proxyServer) {
        return getFullUrl(RequestContext.getRequest(), proxyServer);
    }

    public static String getIp() {
        return getIp(RequestContext.getRequest());
    }

    public static String getIps() {
        return getIps(RequestContext.getRequest());
    }

    public static String getIpAddr() {

        return getIpAddr(RequestContext.getRequest());
    }

    public static InetAddress getLocalInetAddress() {
        try {
            return InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public static ServletContext getServletContext() {
        return RequestContext.getServletContext();
    }

    public static String getBasePath() {
        return getBasePath(RequestContext.getRequest());
    }

    public static String getServerName() {
        return getServerName(RequestContext.getRequest());
    }

    /**
     * 去掉环境前缀,如dev-xx.xx.com,去掉dev-，但是如果是prod,则不能去掉
     *
     * @return
     */
    public static String getServerNameWihoutEntActive() {
        String serverName = getServerName(RequestContext.getRequest());
        String active = EnvironmentUtil.get("spring.profiles.active");
        //非prod才需要替换
        if (!"prod".equals(active)) {
            //有时可能刚好二级域名是dev-或test开头-，所以prod-开头的，为保留域名，不能对外做业务二级域名
            if (serverName.startsWith(active+"-")) {
                serverName = serverName.substring(active.length() + 1);
            }
        }
        return serverName;
    }

    public static String getContextPath() {
        return getContextPath(RequestContext.getRequest());
    }

    public static String get2DoMainName() {
        return get2DoMainName(RequestContext.getRequest());
    }

    /**
     * 获取次域名， 此处次级域名，是主除了当前主域名以外的剩下部分,如 apply.show.tongyeyun.com,得到apply.show
     *
     * @return
     */
    public static String getSubalternDoMainName() {
        String serverName = RequestContext.getRequest().getServerName();
        String url = serverName.replace("http://", "").replace("https://", "").replace("." + CoreUrlConstant.domain,
                "");
        return url;
    }

    public static void deleteCookie(String key) {
        deleteCookie(key, RequestContext.getResponse());
    }

    public static void addCookie(String key, String value, int maxAge) {
        addCookie(key, value, maxAge, RequestContext.getResponse());
    }

    public static void addOrUpdateCookie(String key, String value, int maxAge) {
        Cookie cookie = getCookie(key);
        if (null == cookie) {
            addCookie(key, value, maxAge);
        } else {
            cookie.setValue(value);
            cookie.setMaxAge(maxAge);
            RequestContext.getResponse().addCookie(cookie);
        }
    }

    /**
     * 获取cookie
     *
     * @param key
     * @return
     */
    public static Cookie getCookie(String key) {
        Cookie[] cookies = RequestContext.getRequest().getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(key)) {
                    return cookie;
                }
            }
        }
        return null;
    }

    public static void addCookie(String key, String value, String path, String domain, int maxAge) {
        addCookie(key, value, path, domain, maxAge, RequestContext.getResponse());
    }

    public static void addOrUpdateCookie(String key, String value, String path, String domain, int maxAge) {
        HttpServletResponse response = RequestContext.getResponse();
        Cookie cookie = getCookie(key);
        if (null == cookie) {
            addCookie(key, value, path, domain, maxAge, response);
        } else {
            cookie.setValue(value);
            cookie.setPath(path);
            cookie.setDomain(domain);
            cookie.setMaxAge(maxAge);
            response.addCookie(cookie);
        }
    }


    public static String readCookie(String key) {
        return readCookie(key, RequestContext.getRequest());
    }

    /**
     * 是否为ajax请求
     *
     * @param request
     * @return
     */
    public static boolean isAjaxRequest() {
        return isAjaxRequest(RequestContext.getRequest());
    }

    /**
     * 是否为ajax请求
     *
     * @param request
     * @return
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        //如果标识了是feign请求，则一定不是ajax请求
        String feign = request.getHeader(FeignBasicAuthRequestInterceptor.headerKey);
        if (StringUtils.isNotEmptyString(feign)) {
            return false;
        }

        //http自带的ajax请求标识
        String requestType = request.getHeader("X-Requested-With");
        String accessToken = ServletUtil.getRequestToken(request);
        //如果不是ajax请求，但是要按ajax请求的方式处理，如从node转发的
        //标识ajax可以是header,也可以是 attribute
        String isAjax = request.getHeader(isAjaxKey);
        if (StringUtils.isEmptyString(isAjax)) {
            isAjax = request.getParameter(isAjaxKey);
        }
        if (StringUtils.isEmptyString(isAjax)) {
            isAjax = (String) request.getAttribute(isAjaxKey);
        }
        boolean isAjaxFlag = StringUtils.isNotEmpty(accessToken) || "XMLHttpRequest".equalsIgnoreCase(requestType) || "true".equalsIgnoreCase(isAjax);
        if (!isAjaxFlag && sysProperties != null) {
            isAjaxFlag = sysProperties.getIsAjax();
        }
        return isAjaxFlag;
    }

    public static void printHeaders() {
        HttpServletRequest request = RequestContext.getRequest();
        Enumeration<String> headNames = request.getHeaderNames();
        while (headNames.hasMoreElements()) {
            String headName = headNames.nextElement();
            System.out.println("headName: " + headName + " , value: " + request.getHeader(headName));
        }
    }

    /**
     * 获取客户端设备信息
     *
     * @param request
     * @return
     */
    public static ClientDeviceInfo getClientDeviceInfo(HttpServletRequest request) {
        String deviceBrand = request.getHeader("deviceBrand");
        String deviceSn = request.getHeader("deviceSn");
        String osVersion = request.getHeader("osVersion");
        String appVersion = request.getHeader("appVersion");
        String osType = request.getHeader("osType");
        String createTime = request.getHeader("createTime");
        String provinceName = request.getHeader("provinceName");
        String cityName = request.getHeader("cityName");
        String districtName = request.getHeader("districtName");
        String address = request.getHeader("address");
        String latitude = request.getHeader("latitude");
        String longitude = request.getHeader("longitude");
        String userAgent = request.getHeader("user-agent");
        String pageName = request.getHeader("pageName");
        if (StringUtils.isEmpty(pageName)) {
            pageName = request.getHeader("referer");
        }
        Integer lbsStandardType = request.getIntHeader("lbsStandardType");

        ClientDeviceInfo clientDeviceInfo = new ClientDeviceInfo();
        try {
            if (StringUtils.isNotEmpty(address)) {
                address = URLDecoder.decode(address, "UTF-8");
            }
            if (StringUtils.isNotEmpty(deviceBrand)) {
                clientDeviceInfo.setDeviceBrand(URLDecoder.decode(deviceBrand, "UTF-8"));
            }
            if (StringUtils.isNotEmpty(provinceName)) {
                clientDeviceInfo.setProvinceName(URLDecoder.decode(provinceName, "UTF-8"));
            }
            if (StringUtils.isNotEmpty(cityName)) {
                clientDeviceInfo.setCityName(URLDecoder.decode(cityName, "UTF-8"));
            }
            if (StringUtils.isNotEmpty(districtName)) {
                clientDeviceInfo.setDistrictName(URLDecoder.decode(districtName, "UTF-8"));
            }
            if (StringUtils.isNotEmpty(pageName)) {
                clientDeviceInfo.setPageName(pageName);
            }
            if (StringUtils.isNotEmpty(userAgent)) {
                clientDeviceInfo.setUserAgent(userAgent);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("转码失败", e);
        }

        clientDeviceInfo.setDeviceSn(deviceSn);
        clientDeviceInfo.setOsVersion(osVersion);
        clientDeviceInfo.setAppVersion(appVersion);
        clientDeviceInfo.setOsType(osType);
        clientDeviceInfo.setLbsStandardType(lbsStandardType);
        Date formatDate = null;
        if (StringUtils.isNotEmpty(createTime)) {
            formatDate = DateUtil.getFormatDate(createTime, DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss);
            clientDeviceInfo.setCreateTime(formatDate);
        }

        clientDeviceInfo.setAddress(address);

        if (StringUtils.isNotEmpty(latitude)) {
            try {
                clientDeviceInfo.setLatitude(Double.parseDouble(latitude));
            } catch (NumberFormatException e) {
                log.error("latitude转换异常", e);
            }
        }
        if (StringUtils.isNotEmpty(longitude)) {
            try {
                clientDeviceInfo.setLongitude(Double.parseDouble(longitude));
            } catch (NumberFormatException e) {
                log.error("longitude转换异常", e);
            }
        }
        return clientDeviceInfo;
    }

    /**
     * 获取请求的token
     *
     * @return
     */
    public static String getRequestToken() {
        return getRequestToken(RequestContext.getRequest());
    }

    /**
     * 获取请求的token
     *
     * @param request
     * @return
     */
    public static String getRequestToken(HttpServletRequest request) {
        String token = request.getHeader(JwtUtil.X_ACCESS_TOKEN);
        if (StringUtils.isEmptyString(token)) {
            token = request.getParameter(JwtUtil.X_ACCESS_TOKEN);
        }
        if (StringUtils.isEmptyString(token)) {
            token = (String) request.getAttribute(JwtUtil.X_ACCESS_TOKEN);
        }
        if (StringUtils.isEmptyString(token)) {
            token = request.getParameter("token");
        }
        if (StringUtils.isEmptyString(token)) {
            token = request.getHeader("token");
        }
        if (StringUtils.isEmptyString(token)) {
            token = (String) request.getAttribute("token");
        }
        return token;
    }

    /**
     * 直接向客户端输出文本内容  html
     *
     * @param msg
     */
    public static void writeHtml(String msg) {
        HttpServletResponse response = RequestContext.getResponse();
        response.setContentType(HttpConstant.contentTypeHtmlUTF8);
        doWrite(msg, response);
    }

    /**
     * 直接向客户端输出文本内容  json
     *
     * @param msg
     */
    public static void writeJson(String msg) {
        HttpServletResponse response = RequestContext.getResponse();
        response.setContentType(HttpConstant.contentTypeJsonUTF8);
        doWrite(msg, response);
    }

    /**
     * 直接向客户端输出文本内容  json
     *
     * @param msg
     */
    public static void writeJson(Object obj) {
        String msg = JSONObject.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.PrettyFormat);
        HttpServletResponse response = RequestContext.getResponse();
        response.setContentType(HttpConstant.contentTypeJsonUTF8);
        doWrite(msg, response);
    }

    /**
     * 输出
     *
     * @param msg
     * @param response
     */
    private static void doWrite(String msg, HttpServletResponse response) {
        response.setCharacterEncoding(HttpConstant.defaultEncode);

        try (PrintWriter out = response.getWriter()) {
            out.write(msg);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


